package com.ruben.simplescaffold.utils;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.ruben.simplescaffold.pojo.common.BaseEntity;
import lombok.SneakyThrows;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * mp工具类
 *
 * @author <achao1441470436@gmail.com>
 * @since 2021/5/4 0004 16:35
 */
public class MybatisPlusUtils {
    private MybatisPlusUtils() {
        // Do not new me!
    }

    /**
     * 传入mapper,paramList,源属性，新属性 得到 Collection<新属性> 用于关联表等
     *
     * @param mapper 持久层操作类
     * @param param  查询参数
     * @param origin 源属性
     * @param target 新属性
     * @param <T>    PO
     * @param <I>    T中的源属性
     * @param <R>    T中的新属性
     * @return java.util.Map<I, T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 20:37
     */
    public static <T, I, R> Collection<R> getList(BaseMapper<T> mapper, Supplier<I> param, SFunction<T, I> origin, SFunction<T, R> target) {
        return Optional.ofNullable(param)
                .map(Supplier::get)
                .map(p -> mapper.selectList(new LambdaQueryWrapper<T>().eq(origin, p).select(target)))
                .map(List::parallelStream)
                .map(s -> s.map(target).collect(Collectors.toList()))
                .orElseGet(Collections::emptyList);
    }

    public static <T extends Model<T>, I, R> Collection<R> getList(T entity, SFunction<T, R> target) {
        return Optional.ofNullable(entity)
                .map(e -> e.selectList(new LambdaQueryWrapper<>(entity).select(target)))
                .map(List::parallelStream)
                .map(s -> s.map(target).collect(Collectors.toList()))
                .orElseGet(Collections::emptyList);
    }

    /**
     * 传入mapper,paramList,对应的属性 得到 Map<实体属性, 实体> 用于一对一
     *
     * @param mapper    持久层操作类
     * @param paramList 查询参数
     * @param sFunction 条件
     * @param <T>       PO
     * @param <I>       T中的属性
     * @return java.util.Map<I, T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 20:37
     */
    public static <T, I> Map<I, T> getMapBy(BaseMapper<T> mapper, Collection<I> paramList, SFunction<T, I> sFunction) {
        return Optional.ofNullable(paramList)
                .filter(CollectionUtils::isNotEmpty)
                .map(p -> mapper.selectList(new LambdaQueryWrapper<T>().in(sFunction, p)))
                .map(List::parallelStream)
                .map(s -> s.collect(Collectors.toMap(sFunction, Function.identity(), (a, b) -> b)))
                .orElseGet(Collections::emptyMap);
    }

    public static <T extends Model<T>, I> Map<I, T> getMapBy(T entity, Collection<I> paramList, SFunction<T, I> sFunction) {
        return Optional.ofNullable(paramList)
                .filter(CollectionUtils::isNotEmpty)
                .map(p -> entity.selectList(new LambdaQueryWrapper<T>().in(sFunction, p)))
                .map(List::parallelStream)
                .map(s -> s.collect(Collectors.toMap(sFunction, Function.identity(), (a, b) -> b)))
                .orElseGet(Collections::emptyMap);
    }

    /**
     * 传入mapper,paramList,对应的属性 得到 Map<实体属性, 实体> 用于一对一
     *
     * @param mapper    持久层操作类
     * @param paramList 查询参数
     * @param condition 条件
     * @param need      需要的字段
     * @param <T>       PO
     * @param <I>       T中的属性
     * @return java.util.Map<I, T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 20:37
     */
    public static <T, I, F> Map<I, F> getMapBy(BaseMapper<T> mapper, Collection<I> paramList, SFunction<T, I> condition, SFunction<T, F> need) {
        return Optional.ofNullable(paramList)
                .filter(CollectionUtils::isNotEmpty)
                .map(p -> mapper.selectList(new LambdaQueryWrapper<T>().in(condition, p).select(need)))
                .map(List::parallelStream)
                .map(s -> s.collect(Collectors.toMap(condition, need, (a, b) -> b)))
                .orElseGet(Collections::emptyMap);
    }

    /**
     * 传入mapper,paramList,对应的属性 得到 Map<实体属性, Collection<实体>> 用于一对多
     *
     * @param mapper    持久层操作类
     * @param paramList 查询参数
     * @param sFunction 条件
     * @param <T>       PO
     * @param <I>       T中的属性
     * @return java.util.Map<I, List < T> >
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 20:37
     */
    public static <T, I> Map<I, List<T>> groupBy(BaseMapper<T> mapper, Collection<I> paramList, SFunction<T, I> sFunction) {
        return Optional.ofNullable(paramList)
                .filter(CollectionUtils::isNotEmpty)
                .map(p -> mapper.selectList(new LambdaQueryWrapper<T>().in(sFunction, p)))
                .map(List::parallelStream)
                .map(s -> s.collect(Collectors.groupingBy(sFunction)))
                .orElseGet(Collections::emptyMap);
    }

    /**
     * AR模式传入entity,paramList,对应的属性 得到 Map<实体属性, Collection<实体>> 用于一对多
     *
     * @param entity    AR模式实体类
     * @param paramList 查询参数
     * @param sFunction 条件
     * @param <T>       PO
     * @param <I>       T中的属性
     * @return java.util.Map<I, List < T> >
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 20:37
     */
    public static <T extends Model<T>, I> Map<I, List<T>> groupBy(T entity, Collection<I> paramList, SFunction<T, I> sFunction) {
        return Optional.ofNullable(paramList)
                .filter(CollectionUtils::isNotEmpty)
                .map(p -> entity.selectList(new LambdaQueryWrapper<T>().in(sFunction, p)))
                .map(List::parallelStream)
                .map(s -> s.collect(Collectors.groupingBy(sFunction)))
                .orElseGet(Collections::emptyMap);
    }


    /**
     * 获取表内字段
     *
     * @param function 字段
     * @return java.lang.String
     * @author <achao1441470436@gmail.com>
     * @since 2021/6/29 16:51
     */
    public static <R, T> String getColumn(SFunction<T, R> function) {
        LambdaMeta lambda = LambdaUtils.extract(function);
        TableInfo tableInfo = TableInfoHelper.getTableInfo(lambda.getInstantiatedClass());
        return tableInfo.getFieldList()
                .parallelStream()
                .filter(filed -> PropertyNamer.methodToProperty(lambda.getImplMethodName()).equals(filed.getProperty()))
                .findFirst()
                .map(TableFieldInfo::getColumn)
                .orElseThrow(() -> new MybatisPlusException("未找到该字段"));
    }

    /**
     * 随机查询
     *
     * @param mapper    持久层DAO
     * @param condition 条件
     * @param limit     随机条数
     * @return java.util.Collection<T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/8/10 15:30
     */
    public static <T> Collection<T> getAny(BaseMapper<T> mapper, T condition, Integer limit) {
        LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery(condition);
        long total = mapper.selectCount(wrapper);
        if (limit == null || limit <= 0 || total == 0) {
            return Collections.emptyList();
        }
        List<T> list = Optional.of(limit).filter(l -> l > total).map(l -> mapper.selectList(wrapper)).orElseGet(() -> mapper.selectList(wrapper.last("LIMIT " + RandomUtil.getRandom().nextLong(total - (limit - 1)) + "," + limit)));
        Collections.shuffle(list);
        return list;
    }

    /**
     * AR随机查询
     *
     * @param condition 条件
     * @param limit     随机条数
     * @return java.util.Collection<T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/8/10 15:30
     */
    public static <T extends Model<T>> Collection<T> getAny(T condition, Integer limit) {
        LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery(condition);
        long total = condition.selectCount(wrapper);
        if (limit == null || limit <= 0 || total == 0) {
            return Collections.emptyList();
        }
        List<T> list = Optional.of(limit).filter(l -> l > total).map(l -> condition.selectList(wrapper)).orElseGet(() -> condition.selectList(wrapper.last("LIMIT " + RandomUtil.getRandom().nextLong(total - (limit - 1)) + "," + limit)));
        Collections.shuffle(list);
        return list;
    }

    /**
     * AR模式in查询
     *
     * @param page 分页参数
     * @param ids  ids
     * @param type Class
     * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page<T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/8/16 16:21
     */
    @SneakyThrows
    public static <T extends BaseEntity<T>> IPage<T> selectPageByIds(IPage<T> page, Collection<?> ids, Class<T> type) {
        if (ids.isEmpty()) {
            return page;
        }
        return type.newInstance().selectPage(page, new LambdaQueryWrapper<>(type.newInstance()).in(T::pkVal, ids));
    }


    /**
     * 过滤不需要查询的字段
     *
     * @param wrapper   条件构造器
     * @param functions 字段
     * @return com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/10/12 15:51
     */
    @SafeVarargs
    @SuppressWarnings("unchecked")
    public static <T> LambdaQueryWrapper<T> filterProperties(LambdaQueryWrapper<T> wrapper, SFunction<T, ?>... functions) {
        Set<LambdaMeta> lambdas = Optional.ofNullable(functions).filter(ArrayUtils::isNotEmpty).map(array -> Arrays.stream(array).map(LambdaUtils::extract).collect(Collectors.toSet())).orElseGet(Collections::emptySet);
        Set<String> properties = lambdas.stream().map(LambdaMeta::getImplMethodName).map(PropertyNamer::methodToProperty).collect(Collectors.toSet());
        lambdas.stream().findAny().ifPresent(lam -> wrapper.select((Class<T>) lam.getInstantiatedClass(), i -> !properties.contains(i.getProperty())));
        return wrapper;
    }

    /**
     * 字段转属性名
     *
     * @param functions 字段
     * @return java.util.Set<java.lang.String>
     * @author <achao1441470436@gmail.com>
     * @since 2021/10/18 10:32
     */
    @SafeVarargs
    public static <T> Set<String> functionsToProperties(SFunction<T, Serializable>... functions) {
        return Optional.ofNullable(functions).filter(ArrayUtils::isNotEmpty).map(array -> Arrays.stream(array).map(LambdaUtils::extract).map(LambdaMeta::getImplMethodName).map(PropertyNamer::methodToProperty).collect(Collectors.toSet())).orElseGet(Collections::emptySet);
    }

    /**
     * 为空更新
     *
     * @param entity     实体
     * @param idFunction 主键字段
     * @param functions  字段
     * @return com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<T>
     * @author <achao1441470436@gmail.com>
     * @since 2021/10/18 10:38
     */
    @SafeVarargs
    public static <T> LambdaUpdateWrapper<T> updateNullWrapper(T entity, SFunction<T, Serializable> idFunction, SFunction<T, Serializable>... functions) {
        LambdaUpdateWrapper<T> wrapper = Wrappers.<T>lambdaUpdate().eq(idFunction, idFunction.apply(entity));
        Optional.ofNullable(functions).filter(ArrayUtils::isNotEmpty).ifPresent(array -> Arrays.stream(array).forEach(f -> wrapper.set(f, f.apply(entity))));
        return wrapper;
    }

}
